<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<meta name="keywords" content="Seata,Dubbo,分布式事务,spring" />
	<meta name="description" content="本文讲述通过源码解析Seata-Dubbo传递XID" />
	<!-- 网页标签标题 -->
	<title>源码分析Seata-XID传递 Dubbo篇</title>
  <link rel="shortcut icon" href="/img/seata_logo_small.jpeg"/>
	<link rel="stylesheet" href="/build/blogDetail.css" />
</head>
<body>
	<div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/seata_logo.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/system/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a href="/zh-cn/index.html" target="_self">首页</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">文档</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/docs/developers/developers_dev.html" target="_self">开发者</a></li><li class="menu-item menu-item-normal menu-item-normal-active"><a href="/zh-cn/blog/index.html" target="_self">博客</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/community/index.html" target="_self">社区</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/blog/download.html" target="_self">下载</a></li></ul></div></div></header><section class="blog-content markdown-body"><h1>源码分析Seata-XID传递 Dubbo篇</h1>
<p>本文作者：FUNKYE(陈健斌),杭州某互联网公司主程。</p>
<h1>前言</h1>
<p>​	1.首先来看下包结构,在seata-dubbo和seata-dubbo-alibaba下有统一由TransactionPropagationFilter这个类,分别对应apache-dubbo跟alibaba-dubbo.</p>
<p><img src="/img/blog/20200101203229.png" alt="20200101203229"></p>
<h2>分析源码</h2>
<pre><code class="language-java"><span class="hljs-keyword">package</span> io.seata.integration.dubbo;

<span class="hljs-keyword">import</span> io.seata.core.context.RootContext;
<span class="hljs-keyword">import</span> org.apache.dubbo.common.Constants;
<span class="hljs-keyword">import</span> org.apache.dubbo.common.extension.Activate;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Filter;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Invocation;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Invoker;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Result;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.RpcContext;
<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.RpcException;
<span class="hljs-keyword">import</span> org.slf4j.Logger;
<span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;

<span class="hljs-meta">@Activate</span>(group = {Constants.PROVIDER, Constants.CONSUMER}, order = <span class="hljs-number">100</span>)
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TransactionPropagationFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger LOGGER = LoggerFactory.getLogger(TransactionPropagationFilter.class);

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker&lt;?&gt; invoker, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{
        <span class="hljs-comment">//获取本地XID</span>
        String xid = RootContext.getXID();
        String xidInterceptorType = RootContext.getXIDInterceptorType();
        <span class="hljs-comment">//获取Dubbo隐式传参中的XID</span>
        String rpcXid = getRpcXid();
        String rpcXidInterceptorType = RpcContext.getContext().getAttachment(RootContext.KEY_XID_INTERCEPTOR_TYPE);
        <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
            LOGGER.debug(<span class="hljs-string">"xid in RootContext[{}] xid in RpcContext[{}]"</span>, xid, rpcXid);
        }
        <span class="hljs-keyword">boolean</span> bind = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">if</span> (xid != <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">//传递XID</span>
            RpcContext.getContext().setAttachment(RootContext.KEY_XID, xid);
            RpcContext.getContext().setAttachment(RootContext.KEY_XID_INTERCEPTOR_TYPE, xidInterceptorType);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span> (rpcXid != <span class="hljs-keyword">null</span>) {
                <span class="hljs-comment">//绑定XID</span>
                RootContext.bind(rpcXid);
                RootContext.bindInterceptorType(rpcXidInterceptorType);
                bind = <span class="hljs-keyword">true</span>;
                <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(<span class="hljs-string">"bind[{}] interceptorType[{}] to RootContext"</span>, rpcXid, rpcXidInterceptorType);
                }
            }
        }
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">return</span> invoker.invoke(invocation);
        } <span class="hljs-keyword">finally</span> {
            <span class="hljs-keyword">if</span> (bind) {
                <span class="hljs-comment">//进行剔除已完成事务的XID</span>
                String unbindInterceptorType = RootContext.unbindInterceptorType();
                String unbindXid = RootContext.unbind();
                <span class="hljs-keyword">if</span> (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(<span class="hljs-string">"unbind[{}] interceptorType[{}] from RootContext"</span>, unbindXid, unbindInterceptorType);
                }
                <span class="hljs-comment">//如果发现解绑的XID并不是当前接收到的XID</span>
                <span class="hljs-keyword">if</span> (!rpcXid.equalsIgnoreCase(unbindXid)) {
                    LOGGER.warn(<span class="hljs-string">"xid in change during RPC from {} to {}, xidInterceptorType from {} to {} "</span>, rpcXid, unbindXid, rpcXidInterceptorType, unbindInterceptorType);
                    <span class="hljs-keyword">if</span> (unbindXid != <span class="hljs-keyword">null</span>) {
                        <span class="hljs-comment">//重新绑定XID</span>
                        RootContext.bind(unbindXid);
                        RootContext.bindInterceptorType(unbindInterceptorType);
                        LOGGER.warn(<span class="hljs-string">"bind [{}] interceptorType[{}] back to RootContext"</span>, unbindXid, unbindInterceptorType);
                    }
                }
            }
        }
    }

    <span class="hljs-comment">/**
     * get rpc xid
     * <span class="hljs-doctag">@return</span>
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">getRpcXid</span><span class="hljs-params">()</span> </span>{
        String rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID);
        <span class="hljs-keyword">if</span> (rpcXid == <span class="hljs-keyword">null</span>) {
            rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID.toLowerCase());
        }
        <span class="hljs-keyword">return</span> rpcXid;
    }

}
</code></pre>
<p>​	1.根据源码,我们可以推出相应的逻辑处理</p>
<p><img src="/img/blog/20200101213336.png" alt="20200101213336"></p>
<h2>要点知识</h2>
<p>​	1.Dubbo @Activate注解:</p>
<pre><code class="language-java"><span class="hljs-meta">@Documented</span>
<span class="hljs-meta">@Retention</span>(RetentionPolicy.RUNTIME)
<span class="hljs-meta">@Target</span>({ElementType.TYPE, ElementType.METHOD})
<span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> Activate {
    <span class="hljs-comment">/**
     * Group过滤条件。
     * &lt;br /&gt;
     * 包含{<span class="hljs-doctag">@link</span> ExtensionLoader#getActivateExtension}的group参数给的值，则返回扩展。
     * &lt;br /&gt;
     * 如没有Group设置，则不过滤。
     */</span>
    String[] group() <span class="hljs-keyword">default</span> {};

    <span class="hljs-comment">/**
     * Key过滤条件。包含{<span class="hljs-doctag">@link</span> ExtensionLoader#getActivateExtension}的URL的参数Key中有，则返回扩展。
     * &lt;p/&gt;
     * 示例：&lt;br/&gt;
     * 注解的值 &lt;code&gt;<span class="hljs-doctag">@Activate</span>("cache,validatioin")&lt;/code&gt;，
     * 则{<span class="hljs-doctag">@link</span> ExtensionLoader#getActivateExtension}的URL的参数有&lt;code&gt;cache&lt;/code&gt;Key，或是&lt;code&gt;validatioin&lt;/code&gt;则返回扩展。
     * &lt;br/&gt;
     * 如没有设置，则不过滤。
     */</span>
    String[] value() <span class="hljs-keyword">default</span> {};

    <span class="hljs-comment">/**
     * 排序信息，可以不提供。
     */</span>
    String[] before() <span class="hljs-keyword">default</span> {};

    <span class="hljs-comment">/**
     * 排序信息，可以不提供。
     */</span>
    String[] after() <span class="hljs-keyword">default</span> {};

    <span class="hljs-comment">/**
     * 排序信息，可以不提供。
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">order</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> 0</span>;
}
</code></pre>
<p>可以分析得知,Seata的dubbo过滤器上的注解@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, order = 100),表示dubbo的服务提供方跟消费方都会触发到这个过滤器,所以我们的Seata发起者会产生一个XID的传递,上述流程图跟代码已经很清晰的表示了.</p>
<p>​	2.Dubbo隐式传参可以通过 <code>RpcContext</code> 上的 <code>setAttachment</code> 和 <code>getAttachment</code> 在服务消费方和提供方之间进行参数的隐式传递。</p>
<p>获取:RpcContext.getContext().getAttachment(RootContext.KEY_XID);</p>
<p>传递:RpcContext.getContext().setAttachment(RootContext.KEY_XID, xid);</p>
<h1>总结</h1>
<p>更多源码阅读请访问<a href="http://seata.io/zh-cn/index.html">Seata官网</a></p>
</section><footer class="footer-container"><div class="footer-body"><img src="/img/seata_logo_gray.png"/><p class="docsite-power">website powered by docsite</p><div class="cols-container"><div class="col col-12"><h3>愿景</h3><p>Seata 是一款阿里巴巴开源的分布式事务解决方案，致力于在微服务架构下提供高性能和简单易用的分布式事务服务。</p></div><div class="col col-6"><dl><dt>文档</dt><dd><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">Seata 是什么？</a></dd><dd><a href="/zh-cn/docs/user/quickstart.html" target="_self">快速开始</a></dd><dd><a href="https://github.com/seata/seata.github.io/issues/new" target="_self">报告文档问题</a></dd><dd><a href="https://github.com/seata/seata.github.io" target="_self">在Github上编辑此文档</a></dd></dl></div><div class="col col-6"><dl><dt>资源</dt><dd><a href="/zh-cn/blog/index.html" target="_self">博客</a></dd><dd><a href="/zh-cn/community/index.html" target="_self">社区</a></dd></dl></div></div><div class="copyright"><span>Copyright © 2019 Seata</span></div></div></footer></div></div>
	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
	<script>
		window.rootPath = '';
  </script>
	<script src="/build/blogDetail.js"></script>
	<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?104e73ef0c18b416b27abb23757ed8ee";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
    </script>
</body>
</html>
